<script>
import { uid } from 'quasar'
import { loadCdnSources } from 'src/utils/cdn.js'
import imageMarkSymbol from 'src/utils/mark-symbol.js'
import LocationMap from './map.vue'
import { getQuerySet } from 'src/utils/request.js'

function getGeometryObjectMap () {
  const {
    Marker,
    MultiPoint,
    LineString,
    MultiLineString,
    Polygon,
    MultiPolygon
  } = window.maptalks

  return {
    Point: Marker,
    MultiPoint: MultiPoint,
    LineString: LineString,
    MultiLineString: MultiLineString,
    Polygon: Polygon,
    MultiPolygon: MultiPolygon
  }
}

export default {
  name: 'BookTileViewer',
  components: { LocationMap },
  props: {
    image: {
      type: Object,
      required: true
    }
  },
  emits: ['close'],
  data () {
    return {
      isMobile: window.matchMedia('(pointer:coarse)').matches,
      id: `u${uid()}`,
      current: null,
      map: null,
      tileLayer: null,
      vectorLayer: null,
      vectorVisible: true,
      currentZoom: 0,
      maxZoom: 0,
      popup: '',
      point: null
    }
  },
  watch: {
    image (image) {
      this.current = image.children ? image.children[0] : image
    },
    current () {
      this.setTileLayer()
      this.setVectorLayer()
    },
    vectorVisible (value) {
      if (value) {
        if (this.vectorLayer) {
          this.vectorLayer.show()
        } else {
          this.setVectorLayer()
        }
      } else if (this.vectorLayer) {
        this.vectorLayer.hide()
        this.point = null
      }
    }
  },
  mounted () {
    this.init()
  },
  unmounted () {
    this.map.remove()
    this.map = undefined
  },
  methods: {
    async init () {
      await loadCdnSources()
      this.map = this.getMap(this.id)
      this.map.on('zoomend', ({ to }) => {
        this.currentZoom = to
      })
      this.map.on('touchstart', () => {
        this.$refs.popup.hide()
      })
      this.current = this.image.children ? this.image.children[0] : this.image
    },
    getMap (id, options) {
      options = Object.assign({}, {
        touchPitch: false,
        touchRotate: false,
        panAnimation: false,
        zoomControl: false,
        doubleClickZoom: false,
        attribution: false,
        repeatWorld: false,
        dragPitch: false,
        dragRotate: false,
        spatialReference: {
          projection: 'identity',
          resolutions: [
            512, 256, 128, 64, 32, 16, 8, 4, 2, 1
          ]
        },
        zoom: 0,
        center: [0, 0]
      }, options)
      const { Map } = window.maptalks
      return new Map(id, options)
    },
    getSize (image) {
      const string = image.tile || image.src
      const [, width, height] = string.match(/_(\d+)_(\d+)\.\S+$/)
      return {
        width: parseInt(width),
        height: parseInt(height)
      }
    },
    getBase64Image (img) {
      const canvas = document.createElement('canvas')
      canvas.width = 256
      canvas.height = 256

      const ctx = canvas.getContext('2d')
      ctx.drawImage(img, 0, 0)

      return canvas.toDataURL('image/png')
    },
    getUrlTemplate (image) {
      if (image.tile) {
        return image.tile
      } else {
        return (x, y, z) => {
          const { width, height } = this.getSize(image)
          const offsetX = x * 256
          const offsetY = y * 256
          const resizeWidth = parseInt(width / Math.pow(2, this.maxZoom - z))
          const resize = this.maxZoom === z ? '' : `/resize,w_${resizeWidth}`

          if (x < 0 || y < 0 || offsetX > width || offsetY > height) {
            return ''
          }

          return `${image.src}?x-oss-process=image${resize}/crop,x_${offsetX},y_${offsetY},w_256,h_256`
        }
      }
    },
    getTileLayer (image) {
      const options = {
        urlTemplate: this.getUrlTemplate(image),
        renderer: 'canvas'
      }
      const getBase64Image = this.getBase64Image
      const { TileLayer } = window.maptalks
      const tileLayer = new TileLayer('tile', options)
      tileLayer.on('renderercreate', (e) => {
        e.renderer.loadTileImage = function (img, url) {
          const remoteImage = new Image()
          remoteImage.crossOrigin = 'anonymous'
          remoteImage.onload = () => {
            img.src = getBase64Image(remoteImage)
          }
          remoteImage.src = url
        }
      })

      return tileLayer
    },
    getSpatialReference (width, height) {
      this.maxZoom = this.getMaxZoom(width, height)
      const resolutions = []
      for (let i = 0; i <= this.maxZoom; i++) {
        resolutions.unshift(Math.pow(2, i))
      }
      return {
        projection: 'identity',
        resolutions
      }
    },
    getMaxZoom (width, height) {
      let edge = width > height ? width : height
      let zoom = 0

      for (let i = 0; i < 20; i++) {
        if (edge / 2 > 512) {
          edge = edge / 2
          zoom += 1
        } else {
          return zoom + 1
        }
      }
    },
    setTileLayer () {
      if (this.tileLayer) {
        this.tileLayer.remove()
      }
      const { width, height } = this.getSize(this.current.image || this.current)
      const spatialReference = this.getSpatialReference(width, height)
      const reverseResolutions = [...spatialReference.resolutions].reverse()
      const xLevel = reverseResolutions.findIndex(r => r >= width / window.innerWidth)
      const yLevel = reverseResolutions.findIndex(r => r >= height / window.innerHeight)
      this.currentZoom = reverseResolutions.length - 1 - Math.max(xLevel, yLevel, 0)

      this.tileLayer = this.getTileLayer(this.current.image || this.current)
      this.map.setSpatialReference(spatialReference)
      if (this.current.coordinate) {
        const feature = this.current.coordinate
        if (feature.type === 'Point') {
          this.map.setCenter(this.current.coordinate.coordinates)
          this.map.setZoom(spatialReference.resolutions.length - 1)
        } else {
          const geometryObjectMap = getGeometryObjectMap()
          const geometry = new geometryObjectMap[feature.type](feature.coordinates)
          this.map.fitExtent(geometry.getExtent())
        }
      } else {
        this.map.setCenter([width / 2, -height / 2])
        this.map.setZoom(this.currentZoom)
      }
      this.map.addLayer(this.tileLayer)
    },
    async setVectorLayer () {
      if (this.vectorLayer) {
        this.vectorLayer.clear()
        this.vectorLayer.remove()
        this.vectorLayer = null
      }
      if (!this.current.id) {
        return
      }
      // 抓取数据
      const { status, data } = await getQuerySet({
        api: 'https://api.ditushu.com/book/52/storage/draft/mark/',
        limit: 1000,
        params: {
          filter: 'map:' + this.current.id
        }
      })
      if (status !== 200 || !data.results.length) {
        return
      }
      // 重新生成图层
      const { VectorLayer } = window.maptalks
      this.vectorLayer = new VectorLayer(uid())
      this.map.addLayer(this.vectorLayer)
      // 绘制点
      const geometryObjectMap = getGeometryObjectMap()
      data.results.forEach(item => {
        const feature = item.coordinate
        if (!feature) {
          return false
        }
        const hasGeometry = feature.type === 'Point' && item.lanmark?.geometry?.coordinates
        const symbol = Object.assign({}, imageMarkSymbol[feature.type])
        if (hasGeometry && !this.isMobile) {
          symbol.markerFill = '#FF0000'
          symbol.cursor = 'pointer'
        } else if (this.isMobile) {
          symbol.markerWidth = 12
          symbol.markerHeight = 12
        }
        const geometry = new geometryObjectMap[feature.type](feature.coordinates, {
          id: item._id,
          symbol
        })
        if (hasGeometry && !this.isMobile) {
          geometry.on('click touchend', () => {
            this.point = item.lanmark.geometry.coordinates
          })
        }
        geometry.on('mouseover touchend', ({ domEvent }) => {
          this.popup = item.title
          this.$refs.popup.show(domEvent)
        })
        geometry.on('mouseout', () => {
          this.$refs.popup.hide()
        })
        geometry.addTo(this.vectorLayer)
      })
    }
  }
}
</script>

<template>
  <div class="viewer absolute-full relative-position">
    <div class="absolute-full" :style="{ right: point ? '30%' : 0 }">
      <div :id="id" class="absolute-full" />
      <div v-if="current && current.id" class="absolute-top-left column q-gutter-y-md q-pa-sm" style="z-index: 10">
        <div class="bg-white text-black q-pr-sm shadow-5 border-radius-3">
          <q-toggle
            v-model="vectorVisible"
            color="teal"
            :label="vectorVisible ? '显示标注' : '隐藏标注'"
            size="32px"
          />
        </div>
      </div>
      <q-menu ref="popup" no-parent-event touch-position self="center left" class="bg-transparent no-shadow no-pointer-events">
        <div class="bg-black text-white q-px-md q-py-sm q-ml-md rounded-borders">{{ popup }}</div>
      </q-menu>
    </div>
    <div v-if="point" class="map absolute">
      <location-map :point="point" />
      <div class="absolute-center" style="z-index: 10; left: 0">
        <q-btn dense icon="navigate_next" color="white" text-color="black" @click="point = null">
          <q-tooltip content-class="size-14">收起地图</q-tooltip>
        </q-btn>
      </div>
    </div>
    <div class="absolute-right column q-pa-sm" style="z-index: 10">
      <q-btn dense icon="close" color="white" text-color="black" @click="$emit('close')">
        <q-tooltip content-class="size-14">关闭图片浏览窗口</q-tooltip>
      </q-btn>
      <q-space v-if="!isMobile" />
      <q-btn dense icon="zoom_in" color="white" text-color="black" class="q-mt-md" :disable="currentZoom === maxZoom" @click="map.zoomIn()">
        <q-tooltip content-class="size-14">放大</q-tooltip>
      </q-btn>
      <q-btn dense icon="zoom_out" color="white" text-color="black" class="q-mt-sm" :disable="currentZoom === 0" @click="map.zoomOut()">
        <q-tooltip content-class="size-14">缩小</q-tooltip>
      </q-btn>
    </div>
    <div v-if="current" class="description absolute-bottom-left" :style="{ maxWidth: isMobile ? '100%' : '607px' }">
      <div class="q-px-md q-py-sm">
        <div class="text-h6">{{ current.title }}</div>
        <div class="ellipsis-3-lines">{{ current.description }}</div>
      </div>
      <q-virtual-scroll v-if="image.children" :items="image.children" virtual-scroll-horizontal class="q-px-sm" :style="{ height: isMobile ? '80px' : '120px' }">
        <template #default="{ item }">
          <div :key="item.title" :class="item.class" class="q-mx-sm" @click="current = item">
            <img :src="(item.image.thumbnail || item.image.src) + '?x-oss-process=image/resize,h_104'" :alt="item.title" class="cursor-pointer q-my-sm" :style="{ outline: current === item ? '3px solid #ff0' : '', height: isMobile ? '64px' : '104px' }" />
            <q-tooltip v-if="!isMobile" content-class="size-14" anchor="center middle" self="center middle">{{ item.title }}</q-tooltip>
          </div>
        </template>
      </q-virtual-scroll>
    </div>
  </div>
</template>

<style lang="scss">
.viewer {
  background: rgba(0,0,0,.7);
  z-index: 1000;
  overflow: hidden;
}
.map {
  left: 70%;
  top: 0;
  bottom: 0;
  right: 0;
}
.description {
  background: rgba(0,0,0,.3);
  color: #fff;
  z-index: 9;
}
</style>
